//
//  BleDataTool.swift
//  MMTest
//
//  Created by liujianlong on 2022/9/21.
//

import UIKit
import Foundation
import CoreBluetooth
import TCCKit
let ProductKey = "ProductKey"
var BLE_stamp : Int64 = 0
///旋钮服务
let TouchPad_service = "1F40EAF8-AAB4-14A3-F1BA-F61F35CDDBAA"
///手环服务
let Watch_service = "1F40EAF7-AAB4-14A3-F1BA-F61F35CDDBAA"
///nordict芯片 ota
//let Nordic_OTA_service = "FE59"
let Nordic_OTA_service = "8EC9FE59-F315-4F60-9FB8-838830DAEA50"
///OTA模式下，设备名称
let OTAName = ["XW_OTA","SL_DFU","Goodix_DFU","SL_OTA","XL_OTA"]
///厂商字段
func productName() -> [String] {
    let key = UserDefaults.standard.value(forKey: ProductKey) as? String ?? ""
    let array = key.components(separatedBy: ",")
    if array.count >= 1 && array.first != "" {
        return array
    }else{
        return localProductName
    }
}
var localProductName : [String] {
    switch APP.contry{
    case .china:
        return ["C019","C01B"]
    case .abroad:
        return ["C01D","C01B"]
    case .other:
        return ["C019","C01D","C01B"]
    }
}

enum BLEData{
    /// 3.6.1设置时间
    case setDate
    ///3.3.30 APP 端开启设备定制案参数接口
    case appSetDataToPer(_ cmd:UInt8,_ text:String)
    ///3.5.15 设备端主动上传设备定制案参数接口
    case getDataFromPer
    ///3.2.25 APP 端获取设备固件信息-扩展接口
    case checkFirmwareInfo
    ///3.10.7 APP 端设擦写设备端指定的 FLASH 数据块
    case clearBIN(_ text:String)
    /// 3.10.5 APP传输flash数据给设备
    case sendImgData(_ data:Data)
    //MARK: - nordic OTA  -------------------------------start----------------------------------------
    ///进入nordic芯片ota模式
    case prepareOTA
    
    //MARK: - 富力坤OTA  -------------------------------start----------------------------------------
    /// 步骤2，手机 APP 获取设备当前升级程序的存储起始地址
    case readStorageAddress
    /// 步骤3，APP 发命令让设备擦除升级所需要的地址空间
    case clearStorageAddress(_ address:Int)
    /// 步骤4，传升级数据，App 发送指令至设备
    case sendOTA(_ address:Int, _ bytes:[UInt8])
    /// 步骤5，升级结束，重启 App 发送指令至设备，命令 0x09
    case resetOTA(_ length:Int,_ crc:Int)
    
    //MARK: - 客户MCU OTA  -------------------------------start----------------------------------------
    ///进入nordic芯片ota模式
    case xlbin(_ data:Data)
    
}

extension BLEData{
    var head:String{
        switch self{
        case .readStorageAddress,
                .clearStorageAddress,
                .sendOTA,
                .resetOTA,
                .sendImgData,
                .xlbin:
            return ""
        default:
            let str = self.cmd + self.hex
            let length = "\(str.count / 2)".toHex().fix(8, "0") as String
            let header = "880000" + length + "00"
            return header
        }
    }
    var cmd : String{
        switch self{
        case .setDate:
            return "0401"
        case .appSetDataToPer:
            return "011E"
        case .getDataFromPer:
            return "030F"
        case .checkFirmwareInfo:
            return "0019"
        case .clearBIN:
            return "0807"
            //MARK: - nordic OTA  -------------------------------start----------------------------------------
        case .prepareOTA:
            return "0102"
            //MARK: - 富力坤OTA  -------------------------------start----------------------------------------
        case .readStorageAddress:
            return "01"+"0400"
        case .clearStorageAddress:
            return "03"+"0400"
        case .sendOTA:
            return "05"+"4102"
        case .resetOTA:
            return "09"+"0800"
        default:
            return ""
        }
    }
    
    var ack:String{
        switch self{
        case .checkFirmwareInfo:
            return "001A"
        case .clearBIN:
            return "0808"
        case .sendImgData:
            return "0806"
        default:
            return ""
        }
    }
    
    
    var hex:String{
        switch self{
        case .setDate:
            let stamp = NSDate.currentTimeStamp("yyyy/MM/dd/HH/mm/ss")
            let turple = QCTool.dateHex(Int(stamp) ?? 0)
            return turple.date
        case let .appSetDataToPer(cmd,text):
            let turple = QCTool.crc(cmd: cmd, hex: text)
            
            //校验。计算方法：从“长度”到“数据”结束的全部内容累加，所得结果取反+1，再取低位
            let header = "7FFAAF"
            
            
            if cmd != Int(AIQC.userInfo.cmd){
                BLE_stamp = Int64(Date().timeIntervalSince1970 * 1000)
            }
            return header+turple.data+turple.crc
        case let .clearBIN(string):
            return string
            //return "00FFFFFF"+"00FFFFFF"+"00000000"+"00000000"+"00000000"
            //MARK: - nordic OTA  -------------------------------start----------------------------------------
        case .prepareOTA:
            return "00"
            //MARK: - 富力坤OTA  -------------------------------start----------------------------------------
        case .readStorageAddress:
            return "00000000"
        case let .clearStorageAddress(address):
            return QCTool.reverse("\(address)".toHex().fix(8, "0") as String)
        case let .sendOTA(address, bytes):
            let add_str = QCTool.reverse("\(address)".toHex().fix(8, "0") as String)
            let data_str = bytes.map {String(format: "%02X", $0)}.joined()
            return add_str+"EB00" + data_str
        case let .resetOTA(length, crc):
            let length_str = QCTool.reverse("\(length)".toHex().fix(8, "0") as String)
            var crc_str = String(format: "%0x", crc)
            crc_str = QCTool.reverse(crc_str.fix(8, "0") as String)
            //        let crc_str =  KBTool.reverse("\(crc)".toHex().fix(8, "0") as String)
            //        let data_str = "09"+"0800"+length_str+"af8f5691"
            return length_str+crc_str
        default:
            return ""
        }
    }
    
    var data:Data{
        switch self{
        case let  .sendImgData(data),
            let .xlbin(data):
            return data
        default:
            let str = self.head + self.cmd + self.hex
            return str.hexadecimal() ?? Data()
        }
    }
    
    var service_uuidString:String{
        switch self{
            //MARK: - 富力坤OTA  -------------------------------start----------------------------------------
        case .readStorageAddress,
                .clearStorageAddress,
                .sendOTA,
                .resetOTA:
            return "02F00000-0000-0000-0000-00000000FE00"
        default:
            guard BLE.connectedPeripherals.count > 0, let per = BLE.connectedPeripherals.first, let services = per.services else{
                return ""}
            let array = [TouchPad_service, Watch_service, Nordic_OTA_service]
            for service in array{
                if services.contains(where: {$0.uuid.uuidString == service}){
                    return service
                }
            }
            return ""
        }
    }
    
    var write_char_uuidString:String{
        switch self{
            //MARK: - 富力坤OTA  -------------------------------start----------------------------------------
        case .readStorageAddress,
                .clearStorageAddress,
                .sendOTA,
                .resetOTA:
            return "02F00000-0000-0000-0000-00000000FF01"
        case .sendImgData:
            return "1F400003-AAB4-14A3-F1BA-F61F35CDDBAA"
        default:
            return "1F400001-AAB4-14A3-F1BA-F61F35CDDBAA"
        }
    }
    var read_char_uuidString:String{
        switch self{
            //MARK: - 富力坤OTA  -------------------------------start----------------------------------------
        case .readStorageAddress,
                .clearStorageAddress,
                .sendOTA,
                .resetOTA:
            return "02F00000-0000-0000-0000-00000000FF02"
        default:
            return "1F400002-AAB4-14A3-F1BA-F61F35CDDBAA"
        }
    }
    
    var write:Void{
        if QCTool.noConnect(){return}
        guard BLE.connectedPeripherals.count > 0, let per = BLE.connectedPeripherals.first else{
            guard APP.window?.rootViewController is UIViewController else{return}
            CCHud.start(LS("请先连接设备"), stopTime: 1.5)
            return}
        if !(per.services?.contains(where: {$0.uuid.uuidString == self.service_uuidString}) ?? false){
//            CCHud.start(LS("当前连接的是手环，设置无效"), stopTime: 1.5)
            return
        }
        guard let array = per.requestChar[self.service_uuidString] else {return}
        guard let dict = array.filter({$0.keys.first == self.write_char_uuidString}).first else {return}
        guard dict[self.write_char_uuidString] != nil else {return}
        //                let s = [UInt8](self.data)[0...7].map{String(format: "%02X ", $0)}.joined()
        //        let s = [UInt8](self.data).map{String(format: "%02X ", $0)}.joined()
        //
        //        MyPrint("===================  发送cmd \(s)")
        let task = BLE.write(data: data,
                             service_uuid:service_uuidString,
                             characteristic_uuid:write_char_uuidString,
                             type:.withoutResponse,
                             completion: nil)
        return
    }
}
//MARK: - 蓝牙数据处理
///蓝牙数据处理
class BleDataTool : CCBLEData_update_protocol{
    static let share = BleDataTool()
    private init() {}
    ///数据更新
    func handleData(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
        let bytes = [UInt8](characteristic.value ?? Data())
        
        //MARK: - OTA  -------------------------------start----------------------------------------
        if characteristic.service?.uuid.uuidString == BLEData.readStorageAddress.service_uuidString{
            let opCode = String(format:"%.2x", bytes[1])
            switch opCode{
            case "01":///读取起始位置的回调
                if bytes[0] == 0x0{
                    let address = bytes[4...7].map{String(format:"%.2x", $0)}.joined()
                    NotifyTool.post(BLEData.readStorageAddress.cmd,QCTool.reverse(address))
                }
            case "03":///擦除地址回调
                if bytes[0] == 0x0{
                    let address = bytes[4...7].map{String(format:"%.2x", $0)}.joined()
                    NotifyTool.post(BLEData.clearStorageAddress(0).cmd,QCTool.reverse(address))
                }
            case "05":///写入数据回调
                if bytes[0] == 0x0{
                    let address = bytes[4...7].map{String(format:"%.2x", $0)}.joined()
                    let packet_len = bytes[8...9].map{String(format:"%.2x", $0)}.joined()
                    let param = ["address":QCTool.reverse(address),"packet_len":QCTool.reverse(packet_len)]
                    NotifyTool.post(BLEData.sendOTA(0, [UInt8]()).cmd, nil, param)
                }
                else {}
            default:
                break
            }
            return
        }
        //MARK: - OTA  -------------------------------end----------------------------------------
        
        if bytes.count <= 8{return}
        let heard = bytes[0..<8].map{String(format:"%.2X ",$0)}.joined()
        let payload = bytes[8..<bytes.count].map{String(format:"%.2X|",$0)}.joined()
        //        if bytes.count != 64{
        //            print("返回数据======head=\(heard) payload=\(payload)")
        //            print("返回数据====== \(bytes.map{String(format: "%02X", $0)}.joined(separator: " |"))");
        //        }
        var cmdkey = bytes[8...9].map{String(format:"%.2X",$0)}.joined()
        var param = [String:Any]()
        switch cmdkey{
        case ("0702"):///ACK
            let value = String(format:"%.2X", bytes[10])
            let sendCMDKEY = bytes[11...12].map{String(format:"%.2X",$0)}.joined()
            if value == "01"{
                //print("校验后的包，无误 ===\(bytes[11..<bytes.count].map{String(format:"%.2X ",$0)}.joined())")
                switch (sendCMDKEY){
                case (BLEData.setDate.cmd)://3.6.1,设置日期的ack里面，
                    break
                case (BLEData.appSetDataToPer(0,"").cmd):///3.3.30 APP 端开启设备定制案参数接口
                    break
                default:
                    break
                }
            }
            else if value == "02"{ print("校验后的包，有异常，需要APP端重新再发一次包")}
            else if value == "03"{ print("忙碌中，需要APP端稍后重新再发一次包")}
            else if value == "04"{ print("设备不支持当前协议")}
        case BLEData.checkFirmwareInfo.ack:
            let value = [UInt8](bytes[10..<bytes.count])
            param = ["data":value]
            BLEData.setDate.write
        case (BLEData.getDataFromPer.cmd):///3.5.15设备端主动上传设备定制案参数接口
            let value = [UInt8](bytes[10..<bytes.count])
            let ack = UInt8(bytes[AI_index+7+2])
            let ok = UInt8(bytes[AI_index+7+2+1]) == 0x1
            let hex = bytes[AI_index+7+3..<bytes.count-1].map{String(format: "%.2X", $0)}.joined()
            let crc = UInt8(bytes[bytes.count-1])
            if QCTool.crc(cmd: ack, hex: hex,from: "01",to: "04").crc.toDecimal2() != crc  {
                print("============>  校验码失败 = >  \(bytes.map{String(format: "%.2X", $0)}.joined(separator: " |"))")
                return;
            }
            ///如果小于18字节，那就是回调ok不ok
            if bytes.count <= 20 {
                print(String(format: "========= 设置ack = %.2X  ====结果 = \(ok ? "成功" : "失败")  \(bytes[AI_index+7+2+1])", ack))
                if ok {
                    ///OTA 时候，不去查询0x11
                    if ack != UInt8(AIQC.codeMatch.ack)
                        && ack != UInt8(AIQC.reset.ack)
                        && ack != UInt8(AIQC.boot.ack)
                        && ack != UInt8(AIQC.OTA.ack)
                        && ack != UInt8(AIQC.CRC.ack)
                        && ack != UInt8(AIQC.bootExit.ack)
                        && ack != UInt8(AIQC.IMEI.ack)
                        && ack != UInt8(AIQC.active.ack)
                        && ack != UInt8(AIQC.unActive.ack)
                        && ack != UInt8(AIQC.setHightMode.ack)
                        && ack != UInt8(AIQC.manualSetup.ack){
                        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
                            BLEData.appSetDataToPer(AIQC.userInfo.cmd,"00").write
                        }
                    }
                }else{
                    //CCHud.start(String(format: "发送指令%.2X失败", cmd), stopTime: 1.5)
                }
            }
            if ack == UInt8(AIQC.autoCheckUp.ack){
                let data = [UInt8](value[8..<value.count])
                
                let s = data.map{String(format: "%.2X ", $0)}.joined()
                print("================== \(s)")
            }
            if ack >= UInt8(AIQC.codeMatch.ack) {
                let s = bytes.map{String(format: "%.2X ", $0)}.joined()
                
                MyPrint("   返回的ack = \(String(format: "%02X", ack)) ===S = \(s)   cmdkey = \(cmdkey)")
            }
            //MyPrint("   返回的ack = \(String(format: "%02X", ack))")
            param = ["data":value]
        case BLEData.clearBIN("").ack:
            let value = bytes[10..<11].map{String(format:"%.2X", $0)}.joined()
            param = ["data":value]
        case BLEData.sendImgData(Data()).ack:///3.10.6
            param = ["value":String(format:"%.2x", bytes[10])]
        default:
            break
        }
        NotifyTool.post(cmdkey,param)
    }
}


///爱玩车主板数据的起始位置
let AI_index = 8
struct QCOP{
    var cmd : UInt8 = 0x0
    var ack : UInt8 = 0x0
}
enum AIQC{
    ///状态参数上报(0.5S周期上报) ack: 0x90
    static let infoStatus = QCOP(ack: 0x90)
    ///读取用户全部参数 cmd: 0x11,ack: 0x91
    static let userInfo = QCOP(cmd: 0x11,ack: 0x91)
    ///手动调整 (cmd: 0x12, ack: 0x92)
    static let manualSetup = QCOP(cmd: 0x12, ack: 0x92)
    ///气罐手动打气 (cmd: 0x13 ,ack:0x93)
    static let aeration = QCOP(cmd: 0x13 ,ack:0x93)
    ///设置气罐压力门限 (cmd: 0x14, ack: 0x94)
    static let setPressure = QCOP(cmd: 0x14, ack: 0x94)
    ///设置高度记忆模式 (cmd: 0x15, ack: 0x95)
    static let setHightMode = QCOP(cmd: 0x15, ack: 0x95)
    ///设置ACC启停定时进入休眠时间 (cmd: 0x16, ack: 0x96)
    static let setACCSleep = QCOP(cmd: 0x16, ack: 0x96)
    ///设置电池低压保护电压 (cmd: 0x17, ack: 0x97)
    static let setBatteryLow = QCOP(cmd: 0x17, ack: 0x97)
    ///设置电池低压保护使能 (cmd: 0x18, ack: 0x98)
    static let setBatteryHight = QCOP(cmd: 0x18, ack: 0x98)
    ///设置点火联动设置 (cmd: 0x19, ack: 0x99)
    static let setTurnOn = QCOP(cmd: 0x19, ack: 0x99)
    ///设置熄火联动设置 (cmd: 0x1A, ack: 0x9A)
    static let setTrunOff = QCOP(cmd: 0x1A, ack: 0x9A)
    ///设置预置位保持自动校准 (cmd: 0x1B, ack: 0x9B)
    static let setPreAuto = QCOP(cmd: 0x1B, ack: 0x9B)
    ///设置当前高度到指定预置位 (cmd: 0x1C, ack: 0x9C)
    static let setPreHight = QCOP(cmd: 0x1C, ack: 0x9C)
    ///调整到指定预置位 (cmd: 0x1D, ack: 0x9D)
    static let setPrePos = QCOP(cmd: 0x1D, ack: 0x9D)
    ///系统自检开始 (cmd: 0x1E, ack: 0x9E)
    static let systemCheckStart = QCOP(cmd: 0x1E, ack: 0x9E)
    ///系统自检终止 (cmd: 0x1F, ack: 0x9F)
    static let systemCheckEnd = QCOP(cmd: 0x1F, ack: 0x9F)
    ///设置当前高度为最高值(手动自检模式下起作用) (cmd: 0x20, ack: 0xA0)
    static let setHightToMax = QCOP(cmd: 0x20, ack: 0xA0)
    ///设置当前高度为最低值(手动自检模式下起作用) (cmd: 0x21, ack: 0xA1)
    static let setHightToMin = QCOP(cmd: 0x21, ack: 0xA1)
    ///实时采集上报 (ack: 0xA2)
    static let infoUpdate = QCOP(ack: 0xA2)
    ///设置汽水分离模式 (cmd: 0x23, ack: 0xA3)
    static let setQSFLMode = QCOP(cmd: 0x23, ack: 0xA3)
    ///开始主动干燥 (cmd: 0x24, ack: 0xA4)
    static let autoDrying = QCOP(cmd: 0x24, ack: 0xA4)
    ///一键趴地 (cmd: 0x25, ack: 0xA5)
    static let oneClickLow = QCOP(cmd: 0x25, ack: 0xA5)
    ///一键趴地位置 (cmd: 0x26, ack: 0xA6)
    static let oneClickLowPosition = QCOP(cmd: 0x26, ack: 0xA6)
    ///自检状态上报 (ack: 0xA7)
    static let autoCheckUp = QCOP(ack: 0xA7)
    ///ACC模式设置 (cmd: 0x28, ack: 0xA8)
    static let setACCMode = QCOP(cmd: 0x28, ack: 0xA8)
    ///进入或退出维修模式 (cmd: 0x29, ack: 0xA9)
    static let isFixMode = QCOP(cmd: 0x29, ack: 0xA9)
    ///运行中最低保护气压 (cmd: 0x2A, ack: 0xAA)
    static let protectionPreMin = QCOP(cmd: 0x2A, ack: 0xAA)
    ///清除所有警告 (cmd: 0x2B, ack: 0xAB)
    static let clearWarning = QCOP(cmd: 0x2B, ack: 0xAB)
    ///自动调平功能开关 (cmd: 0x2C, ack: 0xAC)
    static let autoFlatSwitch = QCOP(cmd: 0x2C, ack: 0xAC)
    ///自动调平功能级别 (cmd: 0x2D, ack: 0xAD)
    static let autoFlatLevel = QCOP(cmd: 0x2D, ack: 0xAD)
    ///手动设置预置位 (cmd: 0x2E, ack: 0xAE)
    static let manualHightPre = QCOP(cmd: 0x2E, ack: 0xAE)
    ///激活
    static let active = QCOP(cmd: 0x30, ack: 0xB0)
    ///去激活
    static let unActive = QCOP(cmd: 0x31, ack: 0xB1)
    ///获取设备序列号
    static let IMEI = QCOP(cmd: 0x07, ack: 0x87)
    
    static let IMEI_MCU = QCOP(cmd: 0x61, ack: 0xE1)
    //MARK: - 客户主板OTAZ相关指令
    ///型号匹配
    static let codeMatch = QCOP(cmd: 0x40, ack: 0xC0)
    ///模块复位
    static let reset = QCOP(cmd: 0x41, ack: 0xC1)
    ///进入BOOT
    static let boot = QCOP(cmd: 0x42, ack: 0xC2)
    ///写入数据
    static let OTA = QCOP(cmd: 0x44, ack: 0xC4)
    ///CRC
    static let CRC = QCOP(cmd: 0x45, ack: 0xC5)
    ///退出Boot
    static let bootExit = QCOP(ack: 0xC6)
    static func ackString(ack: UInt8) -> String {
        return String(format: "%04X", ack)
    }
}
